﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace WPF_MVVM_Test.MVVM_ViewModel
{
    /// <summary>
    /// MVVM模式中的ViewModel基类
    /// 为什么需要这个基类：
    /// 1. 实现INotifyPropertyChanged接口，这是WPF数据绑定的核心机制
    /// 2. 当ViewModel中的属性发生变化时，会自动通知UI更新
    /// 3. 避免每个ViewModel都重复实现相同的属性变更通知逻辑
    /// 4. 提供统一的属性变更通知方法，确保代码的一致性
    /// 5. 提供通用的Command创建和属性设置辅助方法
    /// </summary>
    public class BaseViewModel : INotifyPropertyChanged
    {
        /// <summary>
        /// 属性变更事件
        /// 作用：当ViewModel中的属性值发生变化时，触发此事件通知UI进行更新
        /// 为什么需要：WPF的数据绑定机制依赖此事件来知道何时需要刷新界面显示的数据
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 触发属性变更通知的方法
        /// </summary>
        /// <param name="propertyName">发生变更的属性名称，使用CallerMemberName自动获取</param>
        /// 优化说明：
        /// 1. 使用[CallerMemberName]特性，自动获取调用方法的名称，减少硬编码
        /// 2. 子类调用时不需要手动传入属性名，代码更简洁且不易出错
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// 设置属性值的通用方法
        /// </summary>
        /// <typeparam name="T">属性类型</typeparam>
        /// <param name="field">要设置的字段引用</param>
        /// <param name="value">新值</param>
        /// <param name="propertyName">属性名称，自动获取</param>
        /// <returns>如果值发生变化返回true，否则返回false</returns>
        /// 优化说明：
        /// 1. 减少属性设置的重复代码
        /// 2. 自动比较新旧值，避免不必要的通知
        /// 3. 统一的属性设置逻辑，确保代码一致性
        protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value))
                return false;

            field = value;
            OnPropertyChanged(propertyName);
            return true;
        }

        /// <summary>
        /// 创建RelayCommand的辅助方法
        /// </summary>
        /// <param name="execute">命令执行的方法</param>
        /// <param name="canExecute">判断命令是否可执行的方法（可选）</param>
        /// <returns>RelayCommand实例</returns>
        /// 优化说明：
        /// 1. 统一Command的创建方式
        /// 2. 减少子类中的重复代码
        /// 3. 便于后续扩展和维护
        protected RelayCommand CreateCommand(Action execute, Func<bool> canExecute = null)
        {
            return new RelayCommand(execute, canExecute);
        }

        /// <summary>
        /// 创建带参数的RelayCommand的辅助方法
        /// </summary>
        /// <param name="execute">命令执行的方法</param>
        /// <param name="canExecute">判断命令是否可执行的方法（可选）</param>
        /// <returns>RelayCommand实例</returns>
        protected RelayCommand<T> CreateCommand<T>(Action<T> execute, Func<T, bool> canExecute = null)
        {
            return new RelayCommand<T>(execute, canExecute);
        }
    }

    /// <summary>
    /// RelayCommand类 - ICommand接口的简单实现
    /// 为什么需要这个类：
    /// 1. WPF没有提供ICommand的默认实现
    /// 2. 简化命令的创建和使用
    /// 3. 支持委托方式定义命令的执行逻辑
    /// 4. 支持命令的启用/禁用控制
    /// </summary>
    public class RelayCommand : ICommand
    {
        private readonly Action _execute;
        private readonly Func<bool> _canExecute;

        public RelayCommand(Action execute, Func<bool> canExecute = null)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;

        public void Execute(object parameter) => _execute();

        public event EventHandler CanExecuteChanged
        {
            add => CommandManager.RequerySuggested += value;
            remove => CommandManager.RequerySuggested -= value;
        }
    }

    /// <summary>
    /// 带参数的RelayCommand类
    /// 用于需要传递参数的命令场景
    /// </summary>
    public class RelayCommand<T> : ICommand
    {
        private readonly Action<T> _execute;
        private readonly Func<T, bool> _canExecute;

        public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter) => _canExecute?.Invoke((T)parameter) ?? true;

        public void Execute(object parameter) => _execute((T)parameter);

        public event EventHandler CanExecuteChanged
        {
            add => CommandManager.RequerySuggested += value;
            remove => CommandManager.RequerySuggested -= value;
        }
    }
}
